package cn.itsource.hrm.service.impl;

import cn.itsource.hrm.domain.CourseType;
import cn.itsource.hrm.exception.ErrorCode;
import cn.itsource.hrm.exception.ValidUtil;
import cn.itsource.hrm.mapper.CourseTypeMapper;
import cn.itsource.hrm.service.ICourseTypeService;
import cn.itsource.hrm.vo.CrumbsVo;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author yaosang
 * @since 2022-01-06
 */
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {

    @Autowired
    private CourseTypeMapper courseTypeMapper;

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    private static  final  String COURSETYPE_TREEDATA_IN_CACHE = "courseType_treeData_in_cache";//redis的key
    @Override
    public List<CourseType> treeData(Long pid) {
        //加入缓存逻辑
        List<CourseType> courseTypes = (List<CourseType>) redisTemplate.opsForValue()
                .get(COURSETYPE_TREEDATA_IN_CACHE);
        if (courseTypes!=null){
            return courseTypes;
        }else{
            //方案3：循环 500w请求
            RLock lock = null;
           try{
               lock = redissonClient.getLock(COURSETYPE_TREEDATA_IN_CACHE);
               lock.lock();
               //            synchronized (this.getClass()){
               courseTypes = (List<CourseType>) redisTemplate.opsForValue().get(COURSETYPE_TREEDATA_IN_CACHE);
               if (courseTypes==null){
                   // （1）第一个请求会进来
                   List<CourseType> courseTypesFromDb = loadTreeDataByLood(pid);
                   // 1 缓存穿透，方案1:没有数据也放一个null（实现了），方案2：布隆过滤器
                   // 2 缓存击穿，缓存过期一瞬间来高并发。 方案1：永不过期（实现了） 方案2：单机锁（不支持集群）（实现了） //@TODO 方案3：分布式锁（支持集群）
                   // 3 缓存雪崩 ，方案1：缓存永不过期，缓存预热（实现了） 方案2：让各个缓存数据过期时间不一样
                   redisTemplate.opsForValue().set(COURSETYPE_TREEDATA_IN_CACHE,courseTypesFromDb);

                   return courseTypesFromDb;
               }
               //(2)其他请求会进来
               return courseTypes;
//            }
           }finally {
               if (lock != null) {
                   lock.unlock();
               }

           }


        }

        //方案2：递归sql
        //return courseTypeMapper.loadTreeData(pid);
        //方案1：递归代码
        //return loadTreeDataByRecursion(pid);
    }



    //方案3：循环方案
    private List<CourseType> loadTreeDataByLood(Long pid) {
        //1 定义返回列表
        List<CourseType> result = new ArrayList<>();
        //2 查询所有节点
        List<CourseType> allCourseTypes = courseTypeMapper.selectList(null);
        Map<Long,CourseType> courseTypeDto = new HashMap<>();//id-CourseType
        allCourseTypes.forEach(courseType -> {
            courseTypeDto.put(courseType.getId(),courseType);
        });
        //3 遍历所有节点判断是否是一级节点 当前类型pid是传入pid
        allCourseTypes.forEach(courseType -> { //算法时间复杂度 10 O(n2)
            //3.1 如果是放入返回列表
            Long ppid = courseType.getPid();
            if (ppid.longValue()==pid.longValue())
                result.add(courseType);
            //3.2 如果不是，放入父亲儿子集合
            else{
                //1)获取到父亲
                /*
                CourseType parent = allCourseTypes.stream()
                        .filter(courseTypeTmp -> courseTypeTmp.getId().longValue() == ppid.longValue())
                        .findFirst()
                        .get();*/
                /*方案1：ppid依次和所有节点的id进行比对，如果比对上就是父亲
                  CourseType parent = null;
                for (CourseType courseTypeTmp : allCourseTypes) { //10
                    if (courseTypeTmp.getId().longValue()==ppid.longValue()){
                        parent = courseTypeTmp;
                        break;
                    }
                }*/
                //方案2：转换为map再来获取数据 O(2n)
                CourseType parent = courseTypeDto.get(ppid);
                //2)获取到父亲儿子集合，把自己作为一个儿子放进去
                parent.getChildren().add(courseType);
            }

        });
        //4 返回“返回列表”
        return result;

    }


    //递归获取类型树
    //1 自己调用自己
    //2 要有出口
    private List<CourseType> loadTreeDataByRecursion(Long pid) {

        List<CourseType> courseTypes = courseTypeMapper
                .selectList(new EntityWrapper<CourseType>().eq("pid", pid));
        //出口
        if (courseTypes==null|| courseTypes.size()<1)
            return null;

        courseTypes.forEach(courseType -> {
            Long ppid = courseType.getId();
            //自己调用自己
            List<CourseType> children = loadTreeDataByRecursion(ppid);
            courseType.setChildren(children);
        });

        return courseTypes;

    }

    @Override
    public boolean insert(CourseType entity) {
        //数据发生变更，清除缓存，下来查询放缓存并返回
        super.insert(entity);
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        return true;
    }

    @Override
    public boolean deleteById(Serializable id) {
         super.deleteById(id);
        //数据发生变更，清 除缓存，下来查询放缓存并返回
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        return true;
    }

    @Override
    public boolean updateById(CourseType entity) {

        super.updateById(entity);
        //数据发生变更，清除缓存，下来查询放缓存并返回
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        redisTemplate.delete(COURSETYPE_TREEDATA_IN_CACHE);
        return true;
    }


    @Override
    public List<CrumbsVo> queryCrumbs(Long typeId) {
        //0 定义返回类型List
        List<CrumbsVo> list = new ArrayList<>();
        //1 校验
        ValidUtil.assertNotNull(typeId, ErrorCode.CODE_400_COURSE_TYPE_CRUMBS_ILLEGAL);
        //2 通过typeId获取Type进而获取path
        String path = courseTypeMapper.selectById(typeId).getPath();
        //3 通过.分割得到typeIds [1,2,3]
        String[] typeIds = path.split("\\.");
        //4 遍历，每个节点构造CrumbsVo放过入List
        for (String idStr : typeIds) {
            CrumbsVo crumbsVo = new CrumbsVo();
            Long id = Long.valueOf(idStr);
            //4.1通过typeId,查询课程可以构造自己                          1 2 3
            CourseType owner = courseTypeMapper.selectById(id);
            crumbsVo.setOwnerCourseType(owner);
            //4.2 通过自己的pid获取所有的儿子排除自己
            Long pid = owner.getPid();
            List<CourseType> otherCourseTypes = courseTypeMapper
                    .selectList(new EntityWrapper<CourseType>().eq("pid", pid));
            Iterator<CourseType> iterator = otherCourseTypes.iterator();
            while (iterator.hasNext()){
                CourseType courseType = iterator.next();
                if (id.longValue()==courseType.getId().longValue()){
                    iterator.remove();
                    break;
                }
            }
            crumbsVo.setOtherCourseTypes(otherCourseTypes);
           list.add(crumbsVo);
        }
        //5 返回List
        return list;
    }
}
